home *** CD-ROM | disk | FTP | other *** search
/ Libris Britannia 4 / science library(b).zip / science library(b) / PROGRAMM / 3908.ZIP / DPN.TXT < prev    next >
Text File  |  1993-04-19  |  78KB  |  1,781 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18.  
  19.                           DISTRIBUTED
  20.  
  21.                           PROCESSING
  22.  
  23.                             NETWORK
  24.  
  25.  
  26.                           Version 2.0
  27.  
  28.  
  29.  
  30.  
  31.  
  32.  
  33.  
  34.  
  35.              Copyright (c)1993  David Bellchambers
  36.                      All rights reserved.
  37.      TABLE OF CONTENTS
  38.  
  39. 1.   INTRODUCTION
  40.  
  41.      1.1       What Is DPN?
  42.      1.2       Why Would I Need It?
  43.      1.3       System Requirements
  44.      1.4       What Do DPN Programs Look Like?
  45.      1.5       Program Data Exchange
  46.      1.6       So Why Choose DPN?
  47.      1.7       What Software Is Available?
  48.      1.8       Starting From Nothing
  49.  
  50. 2.   INSTALLATION
  51.  
  52. 3.   PREPARING FOR DPN
  53.  
  54.      3.1       The NETDRIVE Environment Variable
  55.      3.2       The NETTIME Environment Variable
  56.  
  57. 4.   USING REMOTE HOSTS
  58.  
  59.      4.1       Starting A Remote Host
  60.      4.2       Stopping A Remote Host
  61.      4.3       Running A Remote Host Under Windows
  62.  
  63. 5.   WRITING APPLICATIONS FOR DPN
  64.  
  65.      5.1       The Turbo Pascal Library
  66.      5.1.1     The Constructor Method
  67.      5.1.2     The Finished Method
  68.      5.1.3     The Kill Method
  69.      5.1.4     The ErrorCode Method
  70.      5.1.5     The ExitCode Method
  71.      5.1.6     The Destructor Method
  72.      5.1.7     Global Functions
  73.  
  74.      5.2       The Turbo C++ Library
  75.      5.2.1     The Constructor Method
  76.      5.2.2     The Finished Method
  77.      5.2.3     The Kill Method
  78.      5.2.4     The ErrorCode Method
  79.      5.2.5     The ExitCode Method
  80.      5.2.6     The Destructor Method
  81.      5.2.7     Global Functions
  82.  
  83.      5.3       DPN Error Codes
  84.  
  85. 6.   DEBUGGING HOST PROGRAMS
  86.  
  87. 7.   SPOOLING BATCH PROGRAMS AND DOS COMMANDS
  88. 8.   DPN SYSTEM MAINTENANCE
  89.  
  90.      8.1       Tidying The Shared Disk
  91.      8.2       The TASKS Utility
  92.  
  93. 9.   BUILDING A CHEAP NODE
  94.  
  95. 10.  DPN IS SHAREWARE
  96.  
  97.      10.1      How To Contact The Author
  98.  
  99. 11.  DISCLAIMER
  100.  
  101. 12.  USEFUL ADDRESSES
  102.  
  103. 13.  ACKNOWLEDGEMENTS
  104. 1.   INTRODUCTION
  105.  
  106. 1.1  What Is DPN?
  107.  
  108. DPN stands for Distributed Processing Network.  It's a way of
  109. increasing the effective computing power of a networked PC by
  110. allowing it to share it's workload among any number of other
  111. PC's on the same network.  The programs can be your own, or
  112. those of others that have been written to run under DPN.  
  113.  
  114. The software included in this package provides everything you
  115. need to set up and run the parallel processing network, and
  116. also to write your own programs in C++ or object-oriented
  117. Pascal to take advantage of the facilities it offers.
  118.  
  119. 1.2  Why Would I Need It?
  120.  
  121. If you are writing programs which do anything computationally
  122. intensive or work on large amounts of data, your programs may
  123. take a long time to run, even if you have one of the better
  124. performing processors.  DPN allows you to make the most of
  125. your available computing resources to cut processing time to a
  126. minimum.  It's also virtually transparent to the user.  DPN
  127. programs are run just like any other, except they can run
  128. considerably faster.
  129.  
  130. DPN is at its most effective with tasks which require a large
  131. amount of data to be processed according to established
  132. routines.  Number-crunching is a speciality of DPN which makes
  133. it ideal for science and engineering, data analysis,
  134. statistics, mathematical research, complex graphics, collating
  135. large databases, even artificial intelligence. Any application
  136. which involves heavy data manipulation is ideal for DPN.
  137.  
  138. As an example, say you are working on a graphical rendering
  139. program which produces screen images from complex 3D model
  140. data.  By itself your computer might take 20 minutes to
  141. generate a single screen picture, but by using DPN to do the
  142. work on two computers, the task takes only 10 minutes!
  143.  
  144. 1.3  System Requirements
  145.  
  146. DPN works by transferring programs and data from a client
  147. machine to one or more host machines, or nodes, via a network. 
  148. The one essential requirement for DPN is that all of the
  149. computers you want to use in this way must be able to access a
  150. common disk drive - a floppy or hard disk drive which can be
  151. read from and written to by any other computer on the network. 
  152. This feature can be provided by a client-server network or a
  153. peer-to-peer network.  DPN will will even work using serial
  154. port networking software as is often used with laptops.  Again
  155. here, the fundamental requirement is that the network software
  156. allows a remote drive on another machine to be accessed as if
  157. it was a local drive, while not affecting normal operation of
  158. the remote machine.  The computers on the network can be of
  159. any specification, from a basic PC with 256K, upwards.
  160.  
  161. If you intend to run existing DPN-aware programs on the
  162. network, then there are no further requirements.  If you
  163. intend using the libraries supplied in this package to write
  164. your own parallel-processing programs, then you will also
  165. require either Borland Turbo Pascal (version 5.5 or higher) or
  166. Borland Turbo C++ (version 2.0 or higher), or any compiler
  167. which is 100% module-compatible (.TPU/.OBJ) with them.
  168.  
  169. 1.4  What Do DPN Programs Look Like?
  170.  
  171. First and foremost, DPN cannot work with just ANY software. 
  172. You can't use it to speed up Windows, accelerate Lotus 1-2-3,
  173. or perform a faster spell-check within Word Perfect for
  174. example.  Programs which are to take advantage of the
  175. parallel-processing facilities of DPN must be written
  176. specifically to include these features.  This makes it
  177. especially suited to research or specialist environments.  
  178.  
  179. You are probably used to the idea of running a single, large
  180. program which does all of the work on a single processor. 
  181. This "serial" processing is precisely why you have to wait so
  182. long for your results.  DPN uses the principle of breaking
  183. down a large program into smaller sub-programs which can be
  184. run on many processors simultaneously.  If you are used to the
  185. idea of programs having functions and procedures then DPN is
  186. very similar, as instead of calling a function somewhere
  187. within a large program, DPN executes the function in the form
  188. of an external program, and because it is an external DOS
  189. program it can be run independently on another machine.  
  190.  
  191. Consider the example of the graphic rendering program given
  192. earlier.  The main program (the "client") may have a line that
  193. goes something like this:
  194.  
  195.                CreateImage(Model);
  196.  
  197. to call the function that creates the full-screen image from
  198. the data structure referred to by 'Model'.  In DPN, it would
  199. look something like this:
  200.  
  201.                Image.Init('CREATE.EXE','');  { Pascal }
  202.  
  203.      or        Task Image("CREATE.EXE","");  // C++
  204.  
  205. That is virtually all there is to it.  CREATE.EXE is the
  206. functional sub-program which computes the image (the "host"
  207. program).  "Image" is an object variable of a pre-defined type
  208. which contains all of the information needed to execute the
  209. task through DPN.  The code written in to the DPN libraries
  210. will pass the process to a waiting pool, and will be duly
  211. processed by the next host processor to become idle.  Your
  212. client program can then get on and do something else while the
  213. host sub-program runs on another machine.  If there is nothing
  214. more to do until the results become available, you can use the
  215. lines:
  216.                While Not Image.Finished Do;  { Pascal }
  217.  
  218.      or        while (!Image.Finished());    //  C++
  219.  
  220. to wait until the task is complete.  In fact, there is nothing
  221. to stop you from starting a number of remote processes,
  222. quitting the main program to do something else, and then run
  223. another program to work on the results which have become
  224. available in the meantime.
  225.  
  226. At this point you may be wondering what could be the advantage
  227. of the example given above. Haven't we just let one machine
  228. stand idle while exactly the same job has been done by another
  229. machine?  Surely this would run even more slowly, with all of
  230. the overhead of transferring files and running programs
  231. remotely?  Well yes, but consider these slightly more involved
  232. examples:
  233.  
  234.                Image.Init('CREATE.EXE','UPPER');  { Pascal }
  235.                CreateImage('LOWER');
  236.  
  237.                Task Image("CREATE.EXE","UPPER");  // C++
  238.                CreateImage("LOWER");
  239.  
  240. Here we have spooled the CREATE.EXE sub-program to run
  241. remotely on another machine, passing it the parameter 'UPPER'. 
  242. Then we execute an internal routine, passing it the parameter
  243. 'LOWER'.  The internal function and the host program perform
  244. exactly the same task, and indeed, much of the source code
  245. will be identical.  The string UPPER has been passed to the
  246. host program as a command line parameter telling it which part
  247. of the screen display to work on.  It has been assumed that
  248. the remote program knows in which file to look for the 3D data
  249. required to create the image.  The important point to note is
  250. that the CreateImage function running on the local machine
  251. will only take half the usual execution time because it's only
  252. working on producing half of the screen.  By the time it has
  253. finished, the remote process will have also completed
  254. rendering the other half of the screen and the resultant data
  255. file can be loaded.  The code to retrieve the results might
  256. look like this:
  257.  
  258.                Display_Lower_Screen;
  259.                While Not Image.Finished Do;       { Pascal }
  260.                Display_Upper_Screen;
  261.  
  262.                Display_Lower_Screen();
  263.                while (!Image.Finished());         // C++
  264.                Display_Upper_Screen();
  265.  
  266. The same end result, but in approximately half the time!  By
  267. now you should be getting a feel for how DPN can help you to
  268. make the most of a limited resource.  If the task could be
  269. distributed over a whole network of machines, with each
  270. machine working on a small portion of the image, it would only
  271. take a fraction of the original time, even allowing for the
  272. data transfer overhead. 
  273.  
  274.  
  275. 1.5  Program Data Exchange
  276.  
  277. In conventional programs, functions are called with arguments
  278. to pass the data they require.  DPN allows arguments to be
  279. passed to host sub-programs through the command line interface
  280. as with conventional DOS programs.  While this is useful for
  281. telling the host program what it must do, it is no good for
  282. transferring large amounts of data.  DPN has to use a
  283. different approach as once a host program is running on a
  284. remote machine it cannot access the data of the client.  DPN
  285. requires that data is passed between processes in the form of
  286. data files stored on a shared disk area.  This data must be
  287. stored in a format which is mutually understood by both the
  288. client and the host programs.  For programs which work with
  289. huge amounts of data, this data is probably already being
  290. stored in disk files instead of memory.
  291.  
  292. 1.6  So Why Choose DPN?
  293.  
  294. It is true that there are many alternative ways of executing
  295. several programs at once on a single PC.  A good example is
  296. the Windows graphical environment, which allows you to create
  297. multiple virtual DOS machines when operating in enhanced mode. 
  298. However, although this allows you to execute several programs
  299. at once, the work is still only being done by one micro-
  300. processor.  Run two programs simultaneously, and the time-
  301. slicing employed by the system means that each program runs at
  302. half speed.  You might as well run the two programs
  303. consecutively at full speed, or even keep it as a complete
  304. stand-alone program - the overall execution time will be the
  305. same.  The same may also be said of any other (single-
  306. processor) multi-tasking operating system, such as Unix or
  307. OS/2.  DPN allows you to utilise the power of the PC's already
  308. available to you and to maximise their potential, no matter
  309. how modest your system.
  310.  
  311. 1.7  What Software Is Available?
  312.  
  313. At present there is very little software available that takes
  314. advantage of DPN.  The market for such software is small and
  315. specialised, and the emphasis is on programmers who need to
  316. make use of DPN to come up with the applications.  Read on to
  317. learn how easy it is to port your existing source code to take
  318. full advantage of the benefits of DPN.  
  319.  
  320. The author is keen to establish a library of public domain and
  321. shareware programs for DPN, and would be delighted to offer
  322. assistance to registered users who are writing software.  
  323.  
  324. 1.8  Starting From Nothing
  325.  
  326. Due to the number and nature of the various PC networking
  327. systems that are available, it has to be assumed that the
  328. reader already has access to an established network installed
  329. between two or more PC's, and that they are able to share a
  330. drive or directory on one of the machines.  If this is not the
  331. case then there are a variety of networking options open to
  332. you, depending largely on how much money you want to spend. 
  333. DPN was written for those users who need more processing power
  334. but cannot afford to spend large amounts of cash, and was
  335. therefore designed to be effective on the most minimal of
  336. systems.  
  337.  
  338. DPN is so simple and effective that you may even wish to
  339. dedicate one or more machines to do nothing else but perform
  340. the tasks required by other, less powerful machines. As you
  341. will see later (section 9, Building A Cheap Node), dedicated
  342. machines offer a considerable cost advantage if you are
  343. setting a system up from scratch or wish to add more power at
  344. minimum cost.
  345.  
  346. Networks come in two basic flavours; "client-server" and
  347. "peer-to-peer".  With a client-server system, a large,
  348. powerful computer acts as a central file store and transfers
  349. files to client machines on request via the network.  This is
  350. the fastest solution but is also the most expensive as one
  351. machine is tied up as a dedicated file-server, and the
  352. software licence fees are not cheap.  Maintenance of the
  353. network is also a non-trivial matter and certainly not for the
  354. novice.
  355.  
  356. A much cheaper alternative is a peer-to-peer network where
  357. each machine makes its local drives and devices available to
  358. other machines on a voluntary basis.  This solution offers
  359. generally slower performance as each machine, in addition to
  360. executing user programs, will also from time to time find
  361. itself accessing its local devices to transfer data to a
  362. another machine.  There are a number of very sophisticated
  363. peer-to-peer networks available, all of which involve
  364. installing networking hardware and software into each
  365. computer.  The cost of this method of networking is typically
  366. around £150 (at time of writing) for each machine on the
  367. network. 
  368.  
  369. For the ultimate in low-cost networking you could try a
  370. software-only package such as $25 Network.  This system uses
  371. the existing serial ports to network up to three computers
  372. (i.e. a client machine + two remote nodes).  The system is
  373. slow compared to other systems, but at approximately £30 for
  374. the whole network (including cables) it represents fantastic
  375. value for money.  DPN was developed using $25 Network and it
  376. remains in everyday use on the authors own system.
  377.  
  378.  
  379. 2.   INSTALLATION
  380.  
  381. Once your network is up and running, installation of DPN is
  382. straightforward.  You will first need to ensure that the drive
  383. which is to hold the installed directory will be accessible to
  384. all of the other machines on the network.  This is important,
  385. as some networks allow restricted access to certain drives
  386. and/or sub-directories and it is essential that all machines
  387. have unrestricted read/write access to the installed DPN
  388. directory and any sub-directories that may be created beneath
  389. it.  
  390.  
  391. The distribution disk contains all of the files required to
  392. get a working DPN system up and running.  You should have
  393. available the following files:
  394.  
  395.                REMOTE.EXE     creates node capable of
  396.                               executing tasks from other
  397.                               machines
  398.                REMOTE.ICO     a Windows icon for REMOTE.EXE
  399.                DPN.TPU        Turbo Pascal unit for use in DPN
  400.                               programs
  401.                DPN.HPP        C++ header file
  402.                DPN_T.OBJ      C++ object code - tiny model
  403.                DPN_S.OBJ      C++ object code - small model
  404.                DPN_C.OBJ      C++ object code - compact model
  405.                DPN_M.OBJ      C++ object code - medium model
  406.                DPN_L.OBJ      C++ object code - large model
  407.                DPN_H.OBJ      C++ object code - huge model
  408.                DPN.TXT        this documentation
  409.                INSTALL.EXE    the installation program
  410.                TASKS.EXE      a task pool monitor
  411.                NOTHING.EXE    a do-nothing testing program
  412.                DPN_TEST.PAS   a demonstration Pascal program
  413.                DPN_TEST.CPP   a demonstration C++ program
  414.                DPN_TEST.EXE   an executable version
  415.                REGISTER.TXT   a software registration form
  416.                README.1ST     last minute additions to the
  417.                               manual
  418.  
  419.  
  420. Insert the distribution disk into a floppy disk drive and
  421. type:
  422.                A:INSTALL      or        B:INSTALL
  423.  
  424. When the program has loaded you will be presented with the
  425. installation box which contains all of the options available
  426. when installing.  Use the mouse to select items within the
  427. box, or if you don't have a mouse you can use the 'Tab' key to
  428. move around.  Pressing the 'Return' key has the same effect as
  429. clicking on the 'Install' button.
  430.  
  431.      1.   Use the "Language" checkboxes to select which
  432.           libraries you wish to install.  By default, both the
  433.           C++ and Pascal libraries will be installed. 
  434.           Checking both boxes off will only install the basic
  435.           DPN system without any language libraries.
  436.  
  437.      2.   Use the "From drive" radio buttons to select the
  438.           drive which contains the installation disk.  By
  439.           default this will be set to the drive where the
  440.           installation program was run from.
  441.  
  442.      3.   Type the name of the DPN directory at the input line
  443.           labelled "To path".  The default setting is C:\DPN. 
  444.           Remember that when the system is up and running, all
  445.           machines on the network must be able to access this
  446.           drive and directory, although the local name may
  447.           differ between machines.
  448.  
  449.      4.   Click on the "OK" button (or press 'Return') when
  450.           you are happy with your selection, or click on the
  451.           "Cancel" button (or press 'Escape') to exit from the
  452.           installation process.
  453.  
  454. Once the files have been copied you have the option of having
  455. the installation program modify the AUTOEXEC.BAT file to
  456. include the NETTIME and NETDRIVE environment variables.  These
  457. are described fully in section 3 below.  For now you can
  458. accept the default value for NETTIME and change it later if
  459. required.  Before making any changes to AUTOEXEC.BAT, the
  460. existing copy is renamed as AUTOEXEC.DPN to allow the old copy
  461. to be restored if required.
  462.  
  463.  
  464. 3.   PREPARING FOR DPN
  465.  
  466. Preparing the system for DPN requires that each machine
  467. connected to the network which is to be used in this way has
  468. two DOS environment variables set.  These should be set
  469. independently for each machine and therefore is ideally done
  470. by the AUTOEXEC.BAT file when the machine is switched on. 
  471. These variables are necessary even if the machine is to be
  472. used as a client rather than a host, so they must always be
  473. available.  The variables are:
  474.  
  475.                NETDRIVE       and       NETTIME
  476.  
  477. 3.1  The NETDRIVE Environment Variable
  478.  
  479. NETDRIVE points to the drive and directory where the DPN
  480. system has been installed.  It should not contain a trailing
  481. '\' character unless it is the root directory.  Remember that
  482. NETDRIVE should point to the local name of the shared drive
  483. for the machine on which it is declared, as not all machines
  484. on the network may refer to the shared directory with the same
  485. drive letter, i.e. while one machine may be able to access it
  486. as E:\DPN, to another machine it may be F:\DPN.  NETDRIVE
  487. should always be set for the specific machine on which it is
  488. declared.  If NETDRIVE points to an invalid drive or directory
  489. the node will not be able to raise itself onto the DPN system.
  490.  
  491. Examples of valid NETDRIVE assignments are:
  492.  
  493.                SET NETDRIVE=E:\DPN
  494.                SET NETDRIVE=N:\ETC\SHARED\DPN
  495.  
  496. 3.2  The NETTIME Environment Variable
  497.  
  498. NETTIME is a value used by the machine whenever it attempts to
  499. access the shared directory.  It is the number of seconds the
  500. remote computer will wait between successive disk accesses
  501. when looking for a task to execute.  In other words, if a
  502. remote host is idle and waiting for a task, it will look every
  503. NETTIME seconds to see if there is a task pending.
  504.  
  505. The value assigned to NETTIME is important to the overall
  506. performance of the system.  If DPN is running on a
  507. client/server network then a value in the range 1-5 is
  508. recommended, depending on the normal level of network usage. 
  509. This is because frequent accessing of the DPN directory on the
  510. file-server is unlikely to slow the system significantly and
  511. means that remote processes can be started almost immediately
  512. after they have been posted.  If DPN is operating on a
  513. peer-to-peer network where there is no dedicated file-server,
  514. then a higher value of NETTIME should be used, typically 5-10
  515. seconds or even more.  The value needs to be higher for a
  516. peer-to-peer network as the continuous access of shared drives
  517. slows down the computer which owns the shared drive while it
  518. services the request.  For a peer-to-peer network you can see
  519. that there is a trade-off between slowing the network down,
  520. and having to wait many seconds for a process to get started.
  521. This "start-up time" is an important consideration when
  522. writing DPN programs.  Is that time overhead going to make it
  523. worthwhile to create the remote process in the first place? 
  524. If the task is so brief as to make this a concern, then the
  525. answer is almost certainly 'No'.  This is an inevitable
  526. compromise of using peer-to-peer networks but one that is
  527. worth accepting considering the cost advantages.  It is
  528. suggested that you start with the typical values given here,
  529. and fine-tune to a more optimal value after some experience
  530. has been gained of using the DPN system.  
  531. 4.   USING REMOTE HOSTS
  532.  
  533. A remote host (or remote processor) is the term for any PC
  534. which is running the remote processor program REMOTE.EXE.  It
  535. is these machines which will be performing the hard work;
  536. picking jobs up as they become available, running them and
  537. returning the results for the client to retrieve.
  538.  
  539. 4.1  Starting A Remote Host
  540.  
  541. Once the environment variables have been set, a host computer
  542. can be activated by typing:
  543.  
  544.                <drive:\dir>REMOTE
  545.  
  546. at the command prompt, where <drive:\dir> is the local name of
  547. the shared DPN directory. If you cannot remember the path from
  548. a particular node, type:
  549.  
  550.                SET
  551.  
  552. and use the value which is displays as being assigned to
  553. NETDRIVE.  If you are running REMOTE.EXE from within a batch
  554. file then you can use the following to automatically find the
  555. REMOTE.EXE program by using the path assigned to NETDRIVE:
  556.  
  557.                %NETDRIVE%REMOTE
  558.  
  559. You have the option to override the default values of the
  560. environment variables when starting REMOTE.EXE:
  561.  
  562.      REMOTE  [ NETDRIVE=<drive/dir> ] [ NETTIME=xx ]
  563.  
  564. but this should rarely be necessary.  If all goes well the
  565. computer will produce a banner declaring that it is now
  566. on-line.  If any problems are detected, the REMOTE program
  567. will abort with an error message explaining the problem.  The
  568. most likely errors to occur at this stage are environment
  569. variables not being correctly set (see section 3 above) or the
  570. node was unable to find the DPN directory. 
  571.  
  572. When a client machine has a task it wants to spool to a remote
  573. host for processing, it places a special task file in a sub-
  574. directory of NETDRIVE called TASKS.  This is the global "pool"
  575. where all tasks are sent to be picked up.  When a remote node
  576. is ready to accept another task it looks to the TASKS
  577. sub-directory and pulls out the next available task file,
  578. which it reads and performs the action specified.  When the
  579. node has completed one task it looks for the next.  If there
  580. are no tasks immediately available, it waits for a while (the
  581. time specified by the environment variable NETTIME) and then
  582. looks again.  This process will continue until the user
  583. interrupts the remote processor, or the remote processor
  584. detects the occurrence of a fatal error.
  585.  
  586. The remote processor maintains a continuously updated display
  587. of the tasks it is running, together with information about
  588. any non-fatal errors encountered.  During setting up and
  589. initialisation of a new system this can be helpful in ensuring
  590. that the remote processor functioning correctly and picking up
  591. tasks from client nodes.
  592.  
  593. 4.2  Stopping A Remote Host
  594.  
  595. Machines which are running REMOTE.EXE are dedicated to
  596. processing remote tasks and may not be used for anything else
  597. (although see the following section "Running A Host Under
  598. Windows").  If you no longer want to keep a particular node
  599. on-line and would like to use the machine for something else,
  600. then taking a node off the system and returning it to normal
  601. use is simple.  Press any key on the keyboard, and when the
  602. currently processing task has been completed REMOTE.EXE will
  603. terminate and the computer will be returned to its normal
  604. function.  However it may be a while before the node is
  605. returned to normal service while it completes the current
  606. task.  It must be stressed that the method above is the only
  607. proper way to remove a node from the DPN network.  Any other
  608. interruption (such as a Ctrl-Alt-Del reset, or pressing the
  609. reset switch) will destroy the current task and leave the
  610. client machine unaware that the job has been lost.  This will
  611. cause it to wait indefinitely for the task to complete, unless
  612. the user has written a routine to avoid this by timing the
  613. remote task.  All other hosts on the network will be
  614. unaffected and will continue to pull new tasks out of the pool
  615. and process them, but the process that was running on the off-
  616. lined machine will be lost completely.  
  617.  
  618. As DPN hosts are so easy to disable, you may wish to consider
  619. disconnecting the keyboard or even isolating the node machine
  620. in a separate room or cupboard if you are concerned about the
  621. security of running tasks, particularly for lengthy program
  622. runs.  This precaution can only be a deterrent to to clumsy or
  623. inquisitive fingers.  Due to the open nature of DPN and the
  624. architecture of the PC it can never be truly secure.
  625.  
  626. 4.3  Running A Remote Host Under Windows
  627.  
  628. REMOTE.EXE is a standard DOS program and will run quite
  629. happily in a DOS session under Microsoft Windows and can even
  630. be run in the background as a minimised icon.  This may
  631. sometimes be desirable as it allows use of a machine even
  632. while it is processing tasks spooled by other users.  
  633.  
  634. Setting up REMOTE.EXE to run smoothly under Windows is quite
  635. simple if you are already familiar with Windows.  You will
  636. need to create a Program Interface File (.PIF extension) using
  637. the PIF Editor supplied with Windows.  Set this file up to run
  638. REMOTE.EXE in the DPN directory.  Ensure that the 'Background
  639. Execution' box is checked 'on'.  You may, if you wish, alter
  640. the processing time offered to this window by selecting the
  641. appropriate 'Advanced Settings' options.  Once completed,
  642. insert this PIF file as a program item in one of your desktop
  643. groups.  The item will default to an MS-DOS icon, which you
  644. may change using the File|Properties option from the main
  645. menu.  An alternative icon (REMOTE.ICO) has been supplied on
  646. the distribution disk.  You can also use this opportunity to
  647. select the 'Run Minimized' option so that the processor does
  648. not clutter the screen.
  649.  
  650. To run the remote processor, simply double-click on it's icon. 
  651. To halt the remote processor, double-click on the minimised
  652. icon which will be somewhere near the bottom of your desktop,
  653. and when the window has been restored and is the active
  654. window, press any key.  This will halt the remote processor
  655. and close the window.  For a more detailed explanation of
  656. starting and stopping the remote processor the reader is
  657. referred to sections 4.1 and 4.2 above.
  658.  
  659. Running the remote processor under Windows can have major
  660. advantages for the user who has a number of machines available
  661. but to which exclusive use of any cannot be guaranteed.  You
  662. can put the remote processor program item into the Startup
  663. group, thereby ensuring that it is always running, although
  664. the remote processor will need to be shut down in the usual
  665. way before the Windows session can be closed.  
  666.  
  667. If the remote processor is running on a very powerful PC (a
  668. 486DX say) it is unlikely that performance will be greatly
  669. affected by the presence of the remote processor and the user
  670. of the host machine may not even be aware that their machine
  671. is being used by other other users to run their programs.  On
  672. slower or busier machines however, both the Windows user and
  673. the owner of the process being served will notice a
  674. degradation in performance.  
  675.  
  676. It is even possible to run multiple instances of REMOTE.EXE on
  677. the same machine using two or more separate DOS sessions. 
  678. While it is perfectly feasible for a single computer to
  679. perform the role of several independent remote nodes using
  680. this method, it is recommended that this only be attempted
  681. with faster machines.  This method can also be useful, as you
  682. could run several instances of REMOTE.EXE on a single machine,
  683. and then run your application on top of them in the fore-
  684. ground.  Here all of the work would be done transparently on
  685. the one machine, although the benefits of using DPN would be
  686. totally negated.  This does, however, offer an alternative to
  687. the user who needs to run DPN-dependent software, but only has
  688. one available PC.
  689. 5.   WRITING APPLICATIONS FOR DPN
  690.  
  691. The first stage in writing any program which will lend itself
  692. to parallel processing is to break your program down into
  693. modules so that you can see exactly where the processing is
  694. most intensive.  This will usually be evident from inspection
  695. although a utility known as a 'profiler' can be useful,
  696. informing you of which functions take the longest to execute. 
  697. These processing-intensive procedures need to be converted
  698. into stand-alone sub-programs so that they can be called by
  699. the main program and spooled to a host for execution. 
  700. Consider this simple program:
  701.  
  702.  
  703.                    MAIN
  704.               ┌────────────┐
  705.               │ Input Data │
  706.               └─────┬──────┘
  707.              ┌──────┴───────┐
  708.              │ Process Data │
  709.              └──────┬───────┘
  710.              ┌──────┴──────┐
  711.              │ Output Data │
  712.              └─────────────┘
  713.  
  714. In this example, the process which will take the most
  715. computing time is the 'Process Data' stage.  The code which
  716. makes up this stage needs to be rewritten as a stand-alone
  717. program which performs the same task.  The following figure
  718. illustrates the process:
  719.  
  720.  
  721.                    MAIN
  722.               ┌────────────┐
  723.               │ Input Data │
  724.               └─────┬──────┘                   PROCESS
  725.              ┌──────┴───────┐  ---->----  ┌──────────────┐
  726.              │ Call PROCESS │             │ Process Data │
  727.              └──────┬───────┘  ----<----  └──────────────┘
  728.              ┌──────┴──────┐
  729.              │ Output Data │
  730.              └─────────────┘
  731.  
  732. As well as being fundamental to the operation of DPN, the
  733. concept of separating processor intensive tasks into stand-
  734. alone programs means a high degree of modularity in your
  735. finished programs.  In the same way that you combine
  736. frequently used functions into units or libraries for use
  737. again and again, the existence of a stand-alone program to
  738. perform a given task means that the same sub-program can be
  739. used by any DPN program.  Once you have completed a
  740. sub-program to perform, for example, matrix operations, the
  741. same sub-program can be called by any DPN program that
  742. requires the use of matrices.
  743.  
  744. Actually re-coding your procedures and functions into separate
  745. programs is not as difficult as it might at first sound.  If
  746. you have followed good structured programming methods then you
  747. will find that most of your functions can be converted into
  748. sub-programs almost on a "cut & paste" approach.  While this
  749. stand-alone capability is essential it does present a problem
  750. when it comes to exchanging data between your main program and
  751. a sub-program.  In conventional programs you might pass a
  752. pointer to a huge array which stores the matrix in some pre-
  753. determined format.  Simply passing a pointer to a sub-program
  754. will not work as the machines are not able to share the same
  755. memory space.  The solution is to have all data to be passed
  756. to a sub-program written to a transfer file in the shared
  757. directory.  The format of this transfer file must be common to
  758. both the client program and the host sub-program.  The first
  759. task of the sub-program then, is to retrieve this data into
  760. it's own memory before starting the process.  When the process
  761. is complete, the sub-program writes the results back to a
  762. transfer file and when the main program is ready, it can read
  763. the results back.  The process now looks something like the
  764. diagram on the next page. 
  765.  
  766.  
  767.                    MAIN                       PROCESS
  768.               ┌────────────┐
  769.               │ Input Data │         - - >>- -
  770.               └─────┬──────┘       /           \
  771.               ┌─────┴──────┐     /         ┌────────────┐
  772.               │ Write Data │   /           │ Read  Data │
  773.               └─────┬──────┘ /             └─────┬──────┘
  774.              ┌──────┴───────┐             ┌──────┴───────┐
  775.              │ Call PROCESS │             │ Process Data │
  776.              └──────┬───────┘             └──────┬───────┘
  777.               ┌─────┴──────┐ \             ┌─────┴──────┐
  778.               │ Read  Data │   \           │ Write Data │
  779.               └─────┬──────┘     \         └────────────┘
  780.              ┌──────┴───────┐      \           /
  781.              │ Output  Data │        - - <<- -
  782.              └──────────────┘
  783.  
  784. This requirement of reading and writing data files is probably
  785. the biggest drawback to modifying existing programs to use
  786. DPN, although you may already be using disk files to store
  787. large amounts of data in a well-established format (such as
  788. .DBF database files) and the few extra lines of code required
  789. to exchange the data are a small price to pay.  The sub-
  790. program, although using transfer files to read and write data,
  791. may also be supplied with command line parameters just like
  792. any DOS program.  This is extremely useful as it allows the
  793. sub-programs to receive any number of operational commands 
  794. together with the all important names of the transfer files. 
  795. It can even be used to transfer single variables to the sub-
  796. program.  An example of a sub-program command line execution
  797. with parameters might look like this:
  798.  
  799.      MATRIX  3RD.MAT  PRODUCT  1ST.MAT  2ND.MAT
  800.  
  801. This example informs the sub-program MATRIX.EXE that the input
  802. data resides in the files 1ST.MAT and 2ND.MAT, that a product
  803. operation should be performed on the two matrices, and that
  804. the results are to be stored in the file 3RD.MAT, for
  805. retrieval by the main program.
  806.  
  807. So sub-programs are virtually identical to the functions they
  808. replace within the main program.  The only thing to be borne
  809. in mind is that all data must be passed to and from sub-
  810. programs using data files, and that all operators and
  811. filenames, and other parameters you need to pass to the sub-
  812. program must be done through the command line parameter
  813. interface.  
  814.  
  815. Another important point regarding sub-programs is that they
  816. should not request any user data from the keyboard, not even
  817. 'Press any key to continue' type prompts.  The whole concept
  818. of the sub-program is that it should be stand-alone and can
  819. run unsupervised.  Ideally it should not provide for any
  820. screen output either.  It may be running on any one of a
  821. number of nodes on a network and it should make its presence
  822. invisible.  Pass it the data it requires, leave it to get on
  823. with the job, then pick up the results as they become
  824. available.  Sub-programs can return a single integer error
  825. code to the client program which may be used to check for
  826. errors.  This is built into the DPN library and is explained
  827. later.  
  828.  
  829. As an aside, you can see that the example given above has been
  830. passed an operator to tell the program what to do with the
  831. data it reads in.  It is sometimes worth making a sub-program
  832. perform a variety of related tasks, in this case matrix
  833. operations, as it allows flexibility in future work, and means
  834. that you don't have to write a separate program for each
  835. operation.  Conversely, you may want to separately code the
  836. functions in order to minimise the size of the sub-program.
  837. The choice is yours.  
  838.  
  839. DPN takes advantage of the object-oriented extensions to Turbo
  840. Pascal and C++ to make the transition to using DPN as easy as 
  841. possible.  The execution of remote tasks centres around a pre-
  842. defined "process object" and its associated methods.  This
  843. process object is defined in the file DPN.TPU (Pascal) and
  844. DPN.HPP (C++) supplied.  The Pascal and C++ libraries are
  845. described separately in the sub-sections below.
  846. 5.1       The Pascal Library
  847.  
  848. In order to make use of the DPN library, your main program
  849. will require the line: 
  850.  
  851.                Uses  DPN;
  852.  
  853. somewhere at the start.  Remember, it is only the main program
  854. executing on the client machine that needs to be DPN aware. 
  855. This unit and its functions are not required within the
  856. remotely executed sub-programs unless you intend to have them
  857. also spool tasks for processing by other nodes.  
  858.  
  859. The DPN.TPU file was compiled with the far call ($F compiler
  860. directive) and overlay code ($O directive) turned on.
  861.  
  862. The process definition object is defined in DPN.TPU as:
  863.  
  864.      Type
  865.           TaskPtr = ^Task;
  866.  
  867.           Task = Object
  868.                Constructor    Init(Prog,Params:String);
  869.                Destructor     Done; Virtual;
  870.                Function       Finished:Boolean;
  871.                Function       ExitCode:Integer;
  872.                Function       ErrorCode:Integer;
  873.                Function       Kill:Integer;
  874.           Private
  875.                ProgName,
  876.                ProgParms,
  877.                JobName:String;
  878.                ExitValue,
  879.                ErrorValue:Integer;
  880.                End;
  881.  
  882. A single instance of the Task object is used to keep track of
  883. the progress of a single remote task.  Instances of the
  884. process object are created in the usual way to any other local
  885. or global variable:
  886.  
  887.                Var
  888.                     Image:Task;
  889.  
  890. Multiple instantiations of the Task object must be created if
  891. you wish to spool more than one remote task at any one time.  
  892.  
  893. 5.1.1     The Constructor Method
  894.  
  895. Having declared the instance of the process object, it must be
  896. initialised using the Init constructor method.  The
  897. constructor takes just two parameters.  The first parameter is
  898. the name of the sub-program that you want to execute remotely. 
  899. This must include the full path to the sub-program and must
  900. also specify an .EXE or .COM extension. Spooling batch files
  901. (.BAT extension) is also possible, but requires a slightly
  902. different approach which is described in a later section. 
  903. Since batch programs are usually short programs which execute
  904. quickly, it is unlikely that you would ever need to spool one
  905. as a remote process, although it can have its uses. 
  906.  
  907. It is important to remember that every remote node on the
  908. network must be able to locate the sub-program and the data
  909. transfer files based on the drive/directory information you
  910. supply.  This is similar to the concept of the shared drive
  911. mentioned earlier, where each node must be able to find the
  912. DPN sub-directory if the system is to work.  The path of the
  913. sub-program must be identical for all the available nodes. 
  914. For example, if you specified:
  915.  
  916.           MyTask.Init('C:\APPS\3D\CREATE.EXE','');
  917.  
  918. then it is vital that all the remote nodes can find this file
  919. by this name.  This is because (unless you only have one
  920. remote node) you can never be exactly sure which host may pick
  921. up the process.  If a host receives an instruction to run a
  922. program it cannot find the executable file for, it will ignore
  923. the process.  This will include process filenames which do not
  924. specify a .EXE or .COM extension. 
  925.  
  926. The second parameter passed to the constructor method is a
  927. string containing the command line parameters you wish to pass
  928. to the sub-program.  This is a single string with a maximum
  929. length of 255 characters.  Usually it will contain the names
  930. of the data transfer files being used to pass data.  If this
  931. is so then it is important to note that the same conditions on
  932. drive and path name apply as for the name of the executable
  933. sub-program.  All the available nodes must be able to find the
  934. drive and directory as specified to it if it is to be able to
  935. load the data and process the task.  If it cannot find the
  936. data, your sub-programs may crash with a "file not found"
  937. error.  You may find it useful to use the DOS SUBST or ASSIGN
  938. commands to create a 'pseudo-drive', whereby all your DPN
  939. applications and sub-programs appear to be on the same
  940. drive/directory to all the available nodes if this cannot be
  941. directly implemented in the network.
  942.  
  943. The constructor method therefore performs all of the work
  944. required to spool the task to a remote host.  When an task is
  945. initialised using the constructor, it is placed in the task
  946. pool and will be picked up for processing by the next host to
  947. become free.
  948.  
  949. Once the process has been spooled to a host, the information
  950. contained within the instance of the Task object is your only
  951. link to the running process.  If the Task object instance is
  952. destroyed within the client program then the remote process
  953. can no longer be traced.  It will still execute on a remote
  954. host in due course and return results as normal, but the
  955. client program will be unable to determine the status of the
  956. process and it will have effectively been forgotten.  The
  957. process object allows the client program to determine when the
  958. remote process has been completed, and to retrieve the exit
  959. code and error code of the remote process on completion.  If
  960. you no longer need to keep track of the remote process then
  961. you may destroy the Task object, and even re-allocate it with
  962. another process, but the original task definition will then be
  963. lost.  This is also the case if the declared object goes out
  964. of scope during execution of the client program.  If you
  965. retain the process instance for the lifetime of the task, then
  966. there are a number of additional Task object methods which are
  967. available for monitoring the spooled process which are
  968. detailed below.
  969.  
  970. It is important to remember that Turbo Pascal does not call
  971. the constructor method of an object at the time of
  972. declaration.  The object is declared in the "var" statement,
  973. and then has to be explicitly initialised using the
  974. constructor.  This means that it is possible to create a task
  975. object and then perform operations on the task before it has
  976. been initialised with any meaningful values.  This must be
  977. avoided at all costs as the results will be unpredictable and
  978. also totally meaningless.  This can be partly circumvented by
  979. the use of dynamically allocated objects:
  980.  
  981.                Var
  982.                     MyTask:TaskPtr;
  983.                Begin
  984.                ...
  985.                MyTask:=New(TaskPtr,Init(Prog,Params));
  986.                ...
  987.  
  988. In this example, declaring MyTask does not create an instance
  989. of the Task object, merely a pointer to one.  The function New
  990. creates a dynamically allocated instance of the Task object,
  991. automatically calling the constructor method with the supplied
  992. parameters.  While this does offer a partial solution, it
  993. still remains the responsibility of the programmer to ensure
  994. that the pointer variable MyTask has been assigned before
  995. attempting to de-reference it.  The same restrictions on the
  996. scope of the pointer also apply as for real instances of any
  997. variable.
  998.  
  999. 5.1.2     The Finished Method
  1000.  
  1001. The Finished method provides a means of determining if a
  1002. remote process has been completed and returns a boolean value
  1003. of 'true' or 'false', i.e.
  1004.  
  1005.                If Image.Finished Then
  1006.                    .....
  1007.  
  1008. Finished will return false only if it is evident that the task
  1009. is still not completed.  Any error encountered by the method,
  1010. such as the total disappearance of the task will cause it to
  1011. return 'true', returning the appropriate error code to the
  1012. task object.  This ensures that any "waiting" code is exited
  1013. before the error code needs to be inspected.
  1014.  
  1015. 5.1.3     The Kill Method
  1016.  
  1017. The Kill method allows a remote process to be terminated
  1018. before it can be executed by a remote host. 
  1019.  
  1020.                Result:=MyTask.Kill;
  1021.  
  1022. This will erase the task definition file from the task pool. 
  1023. Note that the Kill procedure can only stop jobs which are
  1024. still pending and have not been started.  Once a job has been
  1025. picked up by a node and is running, it cannot be interrupted
  1026. short of resetting the node, and this is not recommended. 
  1027. Kill returns a boolean value to indicate the success of the
  1028. operation.  It will return 'true' if the task was successfully
  1029. killed or if it had already completed, and 'false' for all
  1030. other events.  If a call to this method does return 'false',
  1031. the error code should be inspected to reveal the cause of the
  1032. failure.  Kill has no effect if you attempt to kill a process
  1033. that has either been completed or killed previously.  
  1034.  
  1035. 5.1.3     The ErrorCode Method
  1036.  
  1037. ErrorCode returns the integer DPN error code of the last DPN
  1038. operation performed on this task.  Remember that because Task
  1039. is an object, each instance of the object has it's own, built-
  1040. in error code which is totally independent of all other
  1041. instances of the object.  The value of the error code is not
  1042. affected by examining it's value in this way, and an adverse
  1043. value will not affect any subsequent DPN operations.  Possible
  1044. error codes are described in a later section.  An example of
  1045. the function in use is:
  1046.  
  1047.                     Result:=MyTask.ErrorCode;
  1048.  
  1049. 5.1.4     The ExitCode Method
  1050.  
  1051. The ExitCode function returns the integer value returned by
  1052. the completed sub-program on termination.  This value is read
  1053. automatically when the Finished method returns a true value,
  1054. and is the only valid way to access this result.  The exit
  1055. code can be very useful, as it can be used by sub-programs to
  1056. indicate errors encountered during execution, as opposed to
  1057. attempting to retrieve them from data files.  The exit code
  1058. will not always be that supplied by the terminating sub-
  1059. program.  If a system error occurs that is not trapped by the
  1060. sub-program, such as a 'file not found' error, then this will
  1061. cause the sub-program to terminate unexpectedly.  This method
  1062. allows investigation of such errors.  An example of the
  1063. function in use is:
  1064.  
  1065.                     Result:=MyTask.ExitCode;
  1066.  
  1067. 5.1.5     The Destructor Method
  1068.  
  1069. The final method included within the object definition is the
  1070. destructor method.  This method is called when the task is
  1071. completed or no longer needs to be monitored, and the Task
  1072. object is being disposed.  It performs essential clean-up
  1073. after the task, which mainly involves deleting any files
  1074. created during the creation and spooling of the task.  Pascal
  1075. users need to call this method explicitly:
  1076.  
  1077.                MyTask.Done;
  1078.  
  1079.           or   Dispose(MyTaskPtr,Done);  { dynamic objects }
  1080.  
  1081. 5.1.6     Global Functions
  1082.  
  1083. In addition to the task object definition, there are five
  1084. global functions which are not used in conjunction with a task
  1085. but provide general DPN functions:
  1086.  
  1087.           Function       DPN_Pending:Word;
  1088.           Procedure      DPN_Pause;
  1089.           Function       DPN_NetDrive:String;
  1090.           Function       DPN_NetTime:Word;
  1091.           Function       DPN_ErrorMsg(Code:Integer):String;
  1092.  
  1093. DPN_Pending is a function which returns the total number of
  1094. processes waiting in the task pool.  It does not include any
  1095. tasks which are currently running. 
  1096.  
  1097. DPN_Pause is a procedure that waits the number of seconds
  1098. specified by the NETTIME environment variable.  This can be
  1099. very useful and in some cases highly desirable.  On a
  1100. peer-to-peer network, where disk accesses cause the throughput
  1101. of computing to be reduced, the code: 
  1102.  
  1103.                Repeat
  1104.                Until MyTask.Finished;
  1105.  
  1106. would cause the system to access the shared drive
  1107. continuously, looking to see if the process had been complete. 
  1108. On a network such as $25 Network this would cause unacceptable
  1109. slowness and the system may even appear to 'hang'.  Far better
  1110. then to use:
  1111.  
  1112.                While Not MyTask.Finished Do
  1113.                     DPN_Pause;
  1114.  
  1115. The value of NETTIME set for this procedure will have a
  1116. significant bearing on the overall performance of any DPN
  1117. system based on a peer-to-peer network.  The reader is
  1118. referred to the earlier section on setting the environment
  1119. variables for more information. 
  1120.  
  1121. The function DPN_NetTime returns the integer number of seconds
  1122. which have been assigned to the NETTIME environment variable,
  1123. while DPN_NetDrive returns a string containing the NETDRIVE
  1124. environment variable. 
  1125.  
  1126. The last global DPN function is DPN_ErrorMsg.  This is a
  1127. procedure which returns an error message string based on the
  1128. value of the error code supplied.  The messages returned are
  1129. described in section 5.3 below.
  1130.  
  1131. 5.2  The C++ Library
  1132.  
  1133. In order to make use of the DPN library, your program will
  1134. require the line:
  1135.  
  1136.           #include <dpn.hpp>
  1137.  
  1138. somewhere near the start, or if you have not copied the header
  1139. file to the INCLUDE files sub-directory of your compiler:
  1140.  
  1141.           #include "C:\DPN\DPN.HPP"
  1142.  
  1143. or wherever you have installed the DPN software.  During the
  1144. linking stage you will also need to link in the appropriate
  1145. object file (.OBJ extension) for the memory model you are
  1146. using.  There is an object file for each of the six memory
  1147. models, identified with the following file names:
  1148.  
  1149.           DPN_T.OBJ      tiny model
  1150.           DPN_S.OBJ      small model
  1151.           DPN_M.OBJ      medium model
  1152.           DPN_C.OBJ      compact model
  1153.           DPN_L.OBJ      large model
  1154.           DPN_H.OBJ      huge model
  1155.  
  1156.  
  1157. All of the object files were compiled under Turbo C++ version
  1158. 2.0, with the overlay code option turned on.
  1159.  
  1160. Remember, it is only the main program executing on the client
  1161. machine that needs to be DPN aware.  This unit and its
  1162. functions are not required within the remotely executed
  1163. sub-programs unless you intend to have them also spool tasks
  1164. for processing by other nodes.  The definition of the process
  1165. object is described below:
  1166.           class Task {
  1167.                int  ErrorValue,ExitValue;
  1168.                char ProgName[255],ProgParms[255],JobName[255];
  1169.           public:
  1170.                Task(char *Prog,char *Params);
  1171.                virtual ~Task(void);
  1172.                int  Finished(void);
  1173.                int  ExitCode(void);
  1174.                int  ErrorCode(void);
  1175.                int  Kill(void);
  1176.           };
  1177.  
  1178. A single instance of the Task object is used to keep track of
  1179. the progress of a single remote task.  Instances of the
  1180. process object are created in the usual way to any other local
  1181. or global variable:
  1182.  
  1183.                {
  1184.                Task MyTask(Prog,Params);
  1185.                ...
  1186.  
  1187. The parameter supplied as arguments to the definition of
  1188. MyTask are passed automatically to the constructor method as
  1189. the object is defined.  Multiple instantiations of the Task
  1190. object must be created if you wish to spool more than one
  1191. remote task at any one time.
  1192.  
  1193. 5.1.1     The Constructor Method
  1194.  
  1195. The constructor method is called automatically in C++ whenever
  1196. an instance of an object is defined, or a dynamic object is
  1197. allocated.  The constructor takes just two parameters.  The
  1198. first parameter is the name of the sub-program that you want
  1199. to execute remotely.  This must include the full path to the
  1200. sub-program and must also specify an .EXE or .COM extension.
  1201. Spooling batch files (.BAT extension) is also possible, but
  1202. requires a slightly different approach which is described in a
  1203. later section.  Since batch programs are usually short
  1204. programs which execute quickly, it is unlikely that you would
  1205. ever need to spool one as a remote process, although it can
  1206. have its uses. 
  1207.  
  1208. It is important to remember that every remote node on the
  1209. network must be able to locate the sub-program and the data
  1210. transfer files based on the drive/directory information you
  1211. supply.  This is similar to the concept of the shared drive
  1212. mentioned earlier, where each node must be able to find the
  1213. DPN sub-directory if the system is to work.  The path of the
  1214. sub-program must be identical for all the available nodes. 
  1215. For example, if you specified:
  1216.  
  1217.           Task MyTask("C:\\APPS\\3D\\CREATE.EXE","");
  1218.  
  1219. then it is vital that all the remote nodes can find this file
  1220. by this name.  This is because (unless you only have one
  1221. remote node) you can never be exactly sure which host may pick
  1222. up the process.  If a host receives an instruction to run a
  1223. program it cannot find the executable file for, it will ignore
  1224. the process.  This will include process filenames which do not
  1225. specify a .EXE or .COM extension. 
  1226.  
  1227. The second parameter passed to the constructor method is a
  1228. string containing the command line parameters you wish to pass
  1229. to the sub-program.  This is a single string with a maximum
  1230. length of 255 characters.  Usually it will contain the names
  1231. of the data transfer files being used to pass data.  If this
  1232. is so then it is important to note that the same conditions on
  1233. drive and path name apply as for the name of the executable
  1234. sub-program.  All the available nodes must be able to find the
  1235. drive and directory as specified to it if it is to be able to
  1236. load the data and process the task.  If it cannot find the
  1237. data, your sub-programs may crash with a "file not found"
  1238. error.  You may find it useful to use the DOS SUBST or ASSIGN
  1239. commands to create a 'pseudo-drive', whereby all your DPN
  1240. applications and sub-programs appear to be on the same
  1241. drive/directory to all the available nodes if this cannot be
  1242. directly implemented in the network.
  1243.  
  1244. The constructor method performs all of the work required to
  1245. spool the task to a remote host.  When a task is initialised
  1246. the constructor is called automatically and the task is placed
  1247. in the task pool and will be picked up for processing by the
  1248. next host to become free.
  1249.  
  1250. Once the process has been spooled to a host, the information
  1251. contained within the instance of the Task object is your only
  1252. link to the running process.  If the Task object instance is
  1253. destroyed within the client program then the remote process
  1254. can no longer be traced.  It will still execute on a remote
  1255. host in due course and return results as normal, but the
  1256. client program will be unable to determine the status of the
  1257. process and it will have effectively been forgotten.  The
  1258. process object allows the client program to determine when the
  1259. remote process has been completed, and to retrieve the exit
  1260. code and error code of the remote process on completion.  If
  1261. you no longer need to keep track of the remote process then
  1262. you may destroy the Task object, and even re-allocate it with
  1263. another process, but the original task definition will then be
  1264. lost.  This is also the case if the declared object goes out
  1265. of scope during execution of the client program.  If you
  1266. retain the process instance for the lifetime of the task, then
  1267. there are a number of additional Task object methods which are
  1268. available for monitoring the spooled process which are
  1269. detailed below.
  1270.  
  1271. Instances of the task object may also be dynamically
  1272. allocated.  This can be very useful, as often on entering a
  1273. function code block the exact parameters to be supplied to the
  1274. constructor are not known and therefore the object cannot be
  1275. defined.  Dynamically allocated instances allow this to be
  1276. circumvented:
  1277.  
  1278.                {
  1279.                Task *MyTask;
  1280.  
  1281.                     ...
  1282.                     MyTask=new Task(Prog,Params);
  1283.                     ...
  1284.  
  1285. Here, declaring MyTask does not create an instance of the Task
  1286. object, merely a pointer to one.  The operator 'new' creates a
  1287. dynamically allocated instance of the Task object,
  1288. automatically calling the constructor method with the supplied
  1289. parameters.  It is the responsibility of the programmer to
  1290. ensure that the pointer variable MyTask has been assigned
  1291. before attempting to de-reference it.  The same restrictions
  1292. on the scope of the pointer also apply as for real instances
  1293. of any variable.
  1294.  
  1295. 5.1.2     The Finished Method
  1296.  
  1297. The Finished method provides a means of determining if a
  1298. remote process has been completed and returns an integer value
  1299. indicating the success:
  1300.  
  1301.                if (MyTask.Finished()) { .... }
  1302.  
  1303. Finished will return a value of 0 only if it is evident that
  1304. the task is still not completed.  Any error encountered by the
  1305. method, such as the total disappearance of the task will cause
  1306. it to return a value of 1, returning the appropriate error
  1307. code to the task object.  This ensures that any "waiting" code
  1308. is exited before the error code needs to be inspected.
  1309.  
  1310. 5.1.3     The Kill Method
  1311.  
  1312. The Kill method allows a remote process to be terminated
  1313. before it can be executed by a remote host. 
  1314.  
  1315.                Result=MyTask.Kill();
  1316.  
  1317. Note that the Kill procedure can only stop jobs which are
  1318. still pending and have not been started.  Once a job has been
  1319. picked up by a node and is running, it cannot be interrupted
  1320. short of resetting the node, and this is not recommended. 
  1321. Kill returns an integer value to indicate the success of the
  1322. operation.  It will return 0 if the task was successfully
  1323. killed or if it had already completed, and returns 1 for all
  1324. other events.  If a call to this method does return 1, the
  1325. error code should be inspected to reveal the cause of the
  1326. failure.  Kill has no effect if you attempt to kill a process
  1327. that has either been completed or killed previously.  
  1328.  
  1329. 5.1.3     The ErrorCode Method
  1330.  
  1331. ErrorCode returns the integer DPN error code of the last DPN
  1332. operation performed on this task.  Remember that because Task
  1333. is an object, each instance of the object has it's own, built-
  1334. in error code which is totally independent of all other
  1335. instances of the object.  The value of the error code is not
  1336. affected by examining it's value in this way, and an adverse
  1337. value will not affect any subsequent DPN operations.  Possible
  1338. error codes are described in a later section.  An example of
  1339. the function in use is:
  1340.  
  1341.                     Result=MyTask.ErrorCode();
  1342.  
  1343. 5.1.4     The ExitCode Method
  1344.  
  1345. The ExitCode function returns the integer value returned by
  1346. the completed sub-program on termination.  This value is read
  1347. automatically when the Finished method returns a true value,
  1348. and is the only valid way to access this result.  The exit
  1349. code can be very useful, as it can be used by sub-programs to
  1350. indicate errors encountered during execution, as opposed to
  1351. attempting to retrieve them from data files.  The exit code
  1352. will not always be that supplied by the terminating sub-
  1353. program.  If a system error occurs that is not trapped by the
  1354. sub-program, such as a 'file not found' error, then this will
  1355. cause the sub-program to terminate unexpectedly.  This method
  1356. allows investigation of such errors.  An example is:
  1357.  
  1358.                     Result=MyTask.ExitCode();
  1359.  
  1360. 5.1.5     The Destructor Method
  1361.  
  1362. The final method included within the object definition is the
  1363. destructor method.  This method is called implicitly by the
  1364. program when the task object passes out of the scope of the
  1365. program unit in which it is declared, or when a dynamically
  1366. allocated instance of the Task object is disposed.  It
  1367. performs essential clean-up after the task, which mainly
  1368. involves deleting any files created during the spooling of the
  1369. task.  
  1370.  
  1371. 5.1.6     Global Functions
  1372.  
  1373. In addition to the task object definition, there are five
  1374. global functions which are not used in conjunction with a task
  1375. but provide general DPN functions:
  1376.  
  1377.           void DPN_Initialise(void);
  1378.           void DPN_Pause(void);
  1379.           void DPN_NetDrive(char *Dest);
  1380.           char *DPN_ErrorMsg(int Code,char *Dest);
  1381.           int  DPN_NetTime(void);
  1382.           unsigned int DPN_Pending(void);
  1383.  
  1384. DPN_Initialise is a very important function.  It must be
  1385. called before any other DPN functions are used.  This function
  1386. provides vital setting up for the DPN system.  The function
  1387. need only be used once, preferably somewhere near the start of
  1388. your program.  
  1389.  
  1390. DPN_Pending is a function which returns the total number of
  1391. processes waiting in the task pool.  It does not include any
  1392. tasks which are currently running. 
  1393.  
  1394. DPN_Pause is a procedure that waits the number of seconds
  1395. specified by the NETTIME environment variable.  This can be
  1396. very useful and in some cases highly desirable.  On a
  1397. peer-to-peer network, where disk accesses cause the throughput
  1398. of computing to be reduced, the code: 
  1399.  
  1400.                while (!MyTask.Finished());
  1401.  
  1402. would cause the system to access the shared drive
  1403. continuously, looking to see if the process had been complete. 
  1404. On a network such as $25 Network this would cause unacceptable
  1405. slowness and the system may even appear to 'hang'.  Far better
  1406. then to use:
  1407.  
  1408.                while (!MyTask.Finished())
  1409.                     DPN_Pause();
  1410.  
  1411. The value of NETTIME set for this procedure will have a
  1412. significant bearing on the overall performance of any DPN
  1413. system based on a peer-to-peer network.  The reader is
  1414. referred to the earlier section on setting the environment
  1415. variables for more information. 
  1416.  
  1417. The function DPN_NetTime returns the integer number of seconds
  1418. which have been assigned to the NETTIME environment variable,
  1419. while DPN_NetDrive returns a string containing the NETDRIVE
  1420. environment variable. 
  1421.  
  1422. The last global DPN function is DPN_ErrorMsg.  This is a
  1423. function which returns an error message string into the string
  1424. 'Dest' based on the value of the error code supplied.  The
  1425. function returns a pointer to 'Dest'.  The messages returned
  1426. are described below.
  1427.  
  1428. 5.3  DPN Error Codes
  1429.  
  1430.      Value     Meaning
  1431.      -----     -------
  1432.        0       No errors encountered
  1433.       -1       Task file has disappeared
  1434.       -2       TASKS sub-directory in NETDRIVE has been lost
  1435.       -3       Task is still running
  1436.       -4       Error reading/writing to task file
  1437.      -99       Non-specific error
  1438.  
  1439. 6.   DEBUGGING HOST PROGRAMS
  1440.  
  1441. The fact that the remote sub-programs are stand-alone and do
  1442. not provide for any user input means that you can find
  1443. yourself with a problem if a process crashes or returns
  1444. unpredicted results.  The only solution here is extensive
  1445. testing and debugging before using the sub-programs in any
  1446. application and include as much error-trapping as possible so
  1447. that your sub-programs are bullet-proof.  As mentioned above,
  1448. version 2.0 of DPN allows sub-programs to return an integer
  1449. error code directly to the client program which spooled them,
  1450. in addition to the returning data files which the sub-programs
  1451. create.  
  1452.  
  1453. An advantage of writing your own sub-programs is that they can
  1454. be written and tested in your existing programming
  1455. environment.  Test data files can be created and your
  1456. sub-program single-stepped (with watches set on variables if
  1457. required) and the program debugged in the usual manner.  Once
  1458. everything is running smoothly, you will have the confidence
  1459. to use that sub-program in any other projects which require
  1460. it's features - one of the major benefits of such a modular
  1461. approach (as with any programming).  
  1462.  
  1463.  
  1464. 7.   SPOOLING BATCH PROGRAMS AND DOS COMMANDS
  1465.  
  1466. DPN was designed to be effective with large numerical programs
  1467. which would benefit from parallel processing to reduce their
  1468. execution times.  In the rare event that you might want to
  1469. spool a batch file or a DOS command to a node as a remote
  1470. process instead of a .EXE or .COM file, a slightly different
  1471. approach is required to that described above, as in order to
  1472. execute the command the system requires a copy of the command
  1473. line interpreter COMMAND.COM.  This has to be specified in the
  1474. task definition as the name of the remote process.  The name
  1475. of the batch file is passed to COMMAND.COM as a command line
  1476. parameter.  This example shows how you would execute the batch
  1477. file C:\ETC\SILLY.BAT : 
  1478.  
  1479.      {  Pascal  }
  1480.      Silly.Init('C:\COMMAND.COM','/C  C:\ETC\SILLY.BAT');
  1481.  
  1482.      //  C++
  1483.      Task Silly("C:\\COMMAND.COM","/C C:\\ETC\\SILLY.BAT");
  1484.  
  1485. As with spooling any other task, you should ensure that the
  1486. path and name of the command line interpreter is the same on
  1487. all machines, unless you know you will be executing the task
  1488. on one specific machine.  For those familiar with the Borland
  1489. language libraries, the function 'getenv' can be used to get
  1490. the name and path of the command line interpreter from the
  1491. system environment variables, but beware!  The function
  1492. 'getenv' is evaluated before the process is spooled, and as a
  1493. result the command line interpreter will have the drive and
  1494. path of the client machine, not the node on which the task
  1495. will be running.  Use 'getenv' only if you are sure that the
  1496. command line interpreter is the same on all your machines.  If
  1497. all machines have booted from a hard disk drive C: or a floppy
  1498. disk drive A: then this will probably be the case, but where
  1499. machines have booted from a mixture of floppy and hard disk
  1500. drives, problems may be encountered. Alternatively, put a copy
  1501. of COMMAND.COM in the shared DPN directory.  
  1502.  
  1503. The '/C' parameter in front of the batch file name tells the
  1504. interpreter that when the batch file has been completed, the
  1505. command interpreter should unload itself and return control to
  1506. the original version of COMMAND.COM.  Without it, your remote
  1507. host will freeze when the task has been completed. 
  1508.  
  1509. The same system should be employed for any internal DOS
  1510. commands that are to be executed remotely, i.e.: 
  1511.  
  1512.      {  Pascal  }
  1513.      DosTask.Task('C:\COMMAND.COM','/C  COPY A:\*.* C:\');
  1514.  
  1515.      //  C++
  1516.      Task DosTask("C:\\COMMAND.COM","/C COPY A:\\*.* C:\\");
  1517.  
  1518. External DOS commands can be spooled using the conventional
  1519. technique as they are external programs and do not require the
  1520. presence of COMMAND.COM to run. 
  1521.  
  1522.  
  1523. 8.   DPN SYSTEM MAINTENANCE
  1524.  
  1525. The DPN system is basically self-maintaining if the procedures
  1526. and practices described here are followed.  After prolonged
  1527. and extensive periods of use you may find that the system
  1528. begins to slow down.  The extensive disk usage of DPN means
  1529. that the shared DPN drive may become fragmented as multiple
  1530. files are created and destroyed.  You may also find that
  1531. 'rogue' files can get left behind in the DPN directory. 
  1532. Sometimes these are task files which were not cleaned up
  1533. properly by the client program, but more often they are user
  1534. created data files.  In normal usage this will cause no
  1535. problems, but if a degradation in performance becomes apparent
  1536. the solution is very straight-forward.  
  1537.  
  1538. 8.1  Tidying The Hard Disk
  1539.  
  1540. First of all ensure that there are no user tasks which are
  1541. waiting to be completed using the utility TASKS.EXE described
  1542. below, and take all of the nodes off line using the methods
  1543. described in section 4.  When the shutdown process is
  1544. complete, type:
  1545.  
  1546.                DEL  <NETDRIVE>\TASKS
  1547.  
  1548. where NETDRIVE is the shared DPN drive/directory.  If you
  1549. cannot remember where this is, type:
  1550.  
  1551.                SET
  1552.  
  1553. and use the value defined in the environment variables.  When
  1554. the display responds with:
  1555.  
  1556.           All files in sub-directory will be lost!
  1557.           Proceed? (Y/N)
  1558.  
  1559. type 'Y' and the task pool will be flushed clean.  At this
  1560. point, you may wish to consider using a disk defragmenting
  1561. program, such as Norton Utilities' SPEEDISK, in order to
  1562. compress the disk down and linearise the available disk space.
  1563.  
  1564. 8.2  The TASKS Utility
  1565.  
  1566. Included on the software distribution disk is a utility
  1567. program called TASKS.EXE.  This program provides the
  1568. functionality of the DPN function 'DPN_Pending' in a DOS
  1569. program by informing of the number of tasks which are still
  1570. waiting to be picked up by a remote processor.  TASKS.EXE goes
  1571. one step further, and also informs of the number of tasks
  1572. which are presently flagged as running, and also the number of
  1573. completed jobs which are waiting to be picked up by their
  1574. client programs.  It is often these latter files which begin
  1575. to crowd the task pool when client programs do not look after
  1576. their tasks properly.
  1577.  
  1578. To run TASKS.EXE, simply type:
  1579.  
  1580.                     TASKS
  1581.  
  1582. at the DOS prompt and press return.  TASKS excepts no command
  1583. line parameters, except for '/?' which acts as a help switch.
  1584.  
  1585.  
  1586. 9.   BUILDING A CHEAP NODE
  1587.  
  1588. By now you should have a good understanding of DPN and,
  1589. hopefully, will be thinking about preparing your applications
  1590. to take advantage of it.  DPN has always been intended for use
  1591. on an established network, but if you are starting from a very
  1592. small system and funds are limited (and aren't they always?)
  1593. then this section will show you how to establish a simple yet
  1594. highly effective DPN network, if you are prepared to do a 
  1595. little work with a screwdriver.  The basic premise is that a
  1596. remote host requires very little hardware to stand alone as a
  1597. dedicated number crunching machine.  
  1598. A working node can be set up for use with $25 Network using
  1599. the following components:
  1600.  
  1601.                1.  Case inc. PSU
  1602.                2.  Motherboard inc. RAM
  1603.                3.  Floppy disk drive (1.44Mb)
  1604.                4.  Floppy disk controller card
  1605.                5.  Serial interface card
  1606.                6.  $25 Network
  1607.  
  1608. If you wish to connect the node into a more sophisticated
  1609. network you will also need a network adaptor card.
  1610.  
  1611. You can see what a minimalist system it is, with no provision
  1612. for screen output (nodes don't really need it) and no hard
  1613. disk drive.  While the latter may be useful for working with
  1614. large quantities of data, the author's experience of using the
  1615. system has shown that it is easier to keep all programs and
  1616. data in a shared directory on the client machine.  As you will
  1617. recall, it is very important that when spooling a remote task,
  1618. the filename of the task program is the same on ALL of the
  1619. nodes.  This is essential as the user may never be sure which
  1620. node will pick up the task.  At time of writing, the
  1621. components required to build a minimal node could be bought
  1622. new for a total cost of less than £100, not including the
  1623. motherboard.  This means that the balance can be spent on
  1624. ensuring that you have the best processor for the task.  You
  1625. could consider fitting a maths co-processor for faster
  1626. number-crunching (up to ten times faster), or maybe install
  1627. extra memory to create a super-fast RAM disk for working on
  1628. those large data files.  The 'plug-together' approach of DPN
  1629. means that you can add extra nodes whenever you like providing
  1630. you have the basic knowledge required to assemble a PC
  1631. yourself and connect it to your network.  The cost savings can
  1632. be enormous over buying a complete machines with monitors,
  1633. hard disks and keyboards that never get used.  There is great
  1634. scope for expansion too.
  1635.  
  1636. The configuration described above is virtually identical to
  1637. the authors own system which was used to develop DPN. 
  1638. Considerable cost savings were also made by using the $25
  1639. Network software instead of implementing one of the other
  1640. systems of peer-to-peer networking.  This software-based
  1641. network allows up to three machines to shares resources by
  1642. using the serial ports to transfer data between machines.  It
  1643. is certainly very much slower than conventional networks, but
  1644. is also extremely effective and reliable.  The address of the
  1645. UK distributor is listed at the end of this file. 
  1646.  
  1647. If you do not wish to use the $25 Network software, which
  1648. allows only two nodes to be attached to the client and with
  1649. rather slow disk access, then you will need to consider one of
  1650. the peer-to-peer networks.  In addition to the software
  1651. drivers which must be loaded, these also require the presence
  1652. of a dedicated network interface card, although the speed of
  1653. the network is significantly increased. 
  1654.  
  1655. For the configuration described above which does not include a
  1656. hard disk drive, you will need to create a bootable floppy
  1657. disk.  This is done in the usual way using the DOS FORMAT
  1658. command:
  1659.  
  1660.                FORMAT A: /S
  1661.  
  1662. Once this disk has been initialised, create any necessary
  1663. CONFIG.SYS and AUTOEXEC.BAT start-up files on the disk.  Be
  1664. sure to include the DPN environment variables in the
  1665. AUTOEXEC.BAT file.  The CONFIG.SYS file will probably need to
  1666. contain the network software drivers.  You will find that both
  1667. of these files need only be minimal in what they load, as the
  1668. dedicated host does not need to load keyboard drivers or set
  1669. the DOS prompt, etc.  As an example, the AUTOEXEC.BAT file in
  1670. the authors own dedicated node looks like this:
  1671.  
  1672.           NET$25 COM1 SHARE:A      { network software   }
  1673.           SET NETDRIVE=C:\DPN      { the shared drive   }
  1674.           SET NETTIME=15           { slow for peer-peer }
  1675.           :WAITING                 { wait for network   }
  1676.           IF NOT EXIST %NETDRIVE%\REMOTE.EXE GOTO WAITING
  1677.           %NETDRIVE%\REMOTE        { start processing   }
  1678.  
  1679. The same single boot disk can be used to start up each host
  1680. machine.  When the host is closed down it will attempt to read
  1681. from the floppy drive to complete execution of the AUTOEXEC
  1682. file causing a drive error.  This is unimportant as there are
  1683. no more commands to execute, and if a dedicated node has been
  1684. off-lined then it is probably about to be powered down too.
  1685.  
  1686.  
  1687. 10.  DPN IS SHAREWARE
  1688.  
  1689. The DPN software included on this distribution disk is
  1690. shareware.  This means that it is a fully-fledged version of
  1691. the software with nothing omitted.  You are free to make as
  1692. many copies of the software as you like and you may distribute
  1693. them freely, providing that they are distributed as the
  1694. complete set of files.  If required the files may be
  1695. compressed into an archive file for distribution.  This file
  1696. should be called DPN.xxx, where xxx is the suffix used by the
  1697. archiving software.  
  1698.  
  1699. The copyright in this software remains the property of the
  1700. author at all times.  If you use DPN and find it useful you
  1701. are required to register your use of the software with the
  1702. author within 30 days. Registration costs just £15 for
  1703. installation on a single network of unlimited size and
  1704. provides the following benefits:
  1705.  
  1706.      -    a version of the software with the shareware banners
  1707.           disabled
  1708.      -    unlimited software support
  1709.      -    free upgrades as they become available
  1710.  
  1711. Cheques should be sent to the author at the address below,
  1712. together with the name of the registered user, your
  1713. company/university/institution (if applicable), and your full
  1714. postal address.  The file REGISTER.DOC on the distribution
  1715. disk contains a suitable registration form which may be
  1716. printed out.
  1717.  
  1718. 10.1 How To Contact The Author
  1719.  
  1720. Unfortunately I am not in a position to be able to offer
  1721. telephone support at time of writing.  However, I would be
  1722. delighted to answer postal queries from registered users on
  1723. receipt of a stamped, self-addressed envelope.  Enquiries and
  1724. registrations should be sent to: 
  1725.  
  1726.                David Bellchambers
  1727.                7, Croftlands Avenue,
  1728.                Stubbington,
  1729.                Fareham,
  1730.                Hampshire     PO14  2JR
  1731.  
  1732. When writing in with a problem, please include a disk or
  1733. listing of all the relevant files, including node AUTOEXEC.BAT
  1734. files, the DPN program listing itself, and any other
  1735. information you can provide on your DPN installation.  I know
  1736. that getting a system up and running can be a little confusing
  1737. the first time.
  1738.  
  1739. I am very interested in compiling a list of DPN-aware software
  1740. which users are writing and would like to make available to
  1741. other users.  This includes not only complete applications,
  1742. but also utility sub-programs which perform useful tasks and
  1743. which other users might like to include in their applications. 
  1744. Your comments on this, and any other aspect of DPN, would be
  1745. gratefully received.  
  1746.  
  1747. Finally, thank you for taking the time to review this
  1748. software.  I hope it will prove useful to you. 
  1749.  
  1750.  
  1751. 11.  DISCLAIMER
  1752.  
  1753. While very attempt has been made to ensure that this software
  1754. performs according to the specifications and limitations set
  1755. out above, the author cannot accept any responsibility for
  1756. damage or loss of any kind arising from its use.  The
  1757. suitability of this software to any particular task must be
  1758. determined by, and is the responsibility of, the user.
  1759.  
  1760. 12.  USEFUL ADDRESSES
  1761.  
  1762.  
  1763.      For $25 Network :
  1764.  
  1765.                EQ Consultants
  1766.                Allt an Fhionn,
  1767.                St. Fillians,
  1768.                Perthshire    PH6  2NG
  1769.  
  1770.                Tel. (0764) 85220/85225
  1771.                Fax. (0764) 85241
  1772.  
  1773.  
  1774. 13.  ACKNOWLEDGEMENTS
  1775.  
  1776. MS-DOS and Windows are trademarks of Microsoft Corporation.
  1777. IBM PC,XT and OS/2 are trademarks of International Business
  1778. Machines
  1779.  
  1780. All other trademarks acknowledged.
  1781.